package com.sneakxy.cloudbase.platform.utils.fastdfs;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;

import org.apache.commons.lang3.StringUtils;
import org.csource.common.MyException;
import org.csource.common.NameValuePair;
import org.csource.fastdfs.ProtoCommon;
import org.csource.fastdfs.StorageClient1;
import org.csource.fastdfs.StorageServer;
import org.csource.fastdfs.TrackerServer;
import org.csource.fastdfs.UploadStream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class StorageClient2 extends StorageClient1 {

	protected static final Logger LOG = LoggerFactory.getLogger(StorageClient2.class);
	
	public StorageClient2() {
		super();
	}

	public StorageClient2(TrackerServer trackerServer, StorageServer storageServer) {
		super(trackerServer, storageServer);
	}

	public int modifyFile(String groupName, String appenderFilename, InputStream is, long fileOffset,
			long fileSize) throws IOException, MyException {
		try {
			return this.do_modify_file(groupName, appenderFilename, fileOffset, fileSize,
					new UploadStream(is, fileSize));
		} finally {
			is.close();
		}
	}

	public String[] uploadFile(InputStream is, long fileSize, String fileExtName, NameValuePair[] metaList)
			throws IOException, MyException {
		final String group_name = null;
		return this.uploadFile(group_name, is, fileSize, fileExtName, metaList);
	}

	protected String[] uploadFile(String groupName, InputStream is, long fileSize, String fileExtName,
			NameValuePair[] metaList) throws IOException, MyException {
		final byte cmd = ProtoCommon.STORAGE_PROTO_CMD_UPLOAD_FILE;
		return this.uploadFile(cmd, groupName, is, fileSize, fileExtName, metaList);
	}

	protected String[] uploadFile(byte cmd, String groupName, InputStream is, long fileSize, String fileExtName,
			NameValuePair[] metaList) throws IOException, MyException {
		try {
			return this.do_upload_file(cmd, groupName, null, null, fileExtName, fileSize,
					new UploadStream(is, fileSize), metaList);
		} finally {
			is.close();
		}
	}

	public String[] uploadFile(String groupName, String masterFilename, String prefixName, InputStream is,
			long fileSize, String fileExtName, NameValuePair[] metaList) throws IOException, MyException {
		if (StringUtils.isBlank(groupName)
				|| StringUtils.isBlank(masterFilename) || (prefixName == null)
				|| fileExtName == null) {
			throw new MyException("invalid arguement");
		}
		try {
			return this.do_upload_file(ProtoCommon.STORAGE_PROTO_CMD_UPLOAD_SLAVE_FILE, groupName, masterFilename,
					prefixName, fileExtName, fileSize, new UploadStream(is, fileSize), metaList);
		} finally {
			is.close();
		}
	}

	public String[] uploadAppenderFile(InputStream is, long fileSize, String fileExtName,
			NameValuePair[] metaList) throws IOException, MyException {
		final String groupName = null;
		return this.uploadAppenderFile(groupName, is, fileSize, fileExtName, metaList);
	}

	protected String[] uploadAppenderFile(String groupName, InputStream is, long fileSize, String fileExtName,
			NameValuePair[] metaList) throws IOException, MyException {
		final byte cmd = ProtoCommon.STORAGE_PROTO_CMD_UPLOAD_APPENDER_FILE;
		return this.uploadFile(cmd, groupName, is, fileSize, fileExtName, metaList);
	}

	public int appendFile(String groupName, String appenderFilename, InputStream is, long fileSize)
			throws IOException, MyException {
		try {
			return this.do_append_file(groupName, appenderFilename, fileSize, new UploadStream(is, fileSize));
		} finally {
			is.close();
		}
	}

	public String uploadFile2(InputStream is, long fileSize, String fileExtName, NameValuePair[] metaList)
			throws IOException, MyException {
		String[] parts = this.uploadFile(is, fileSize, fileExtName, metaList);
		if (parts != null) {
			return parts[0] + SPLIT_GROUP_NAME_AND_FILENAME_SEPERATOR + parts[1];
		} else {
			return null;
		}
	}

	public String uploadFile2(String groupName, InputStream is, long fileSize, String fileExtName,
			NameValuePair[] metaList) throws IOException, MyException {
		String[] parts = this.uploadFile(groupName, is, fileSize, fileExtName, metaList);
		if (parts != null) {
			return parts[0] + SPLIT_GROUP_NAME_AND_FILENAME_SEPERATOR + parts[1];
		} else {
			return null;
		}
	}

	public int downloadFile(String groupName, String remoteFilename, OutputStream out) throws IOException, MyException {
		final long fileOffset = 0;
		final long downloadBytes = 0;
		return this.downloadFile(groupName, remoteFilename, fileOffset, downloadBytes, out);
	}

	public int downloadFile(String groupName, String remoteFilename, long fileOffset, long downloadBytes,
			OutputStream out) throws IOException, MyException {
		boolean bNewConnection = this.newReadableStorageConnection(groupName, remoteFilename);
		Socket storageSocket = this.storageServer.getSocket();
		ProtoCommon.RecvHeaderInfo header;
		try (OutputStream os = out) {
			this.errno = 0;
			this.send_download_package(groupName, remoteFilename, fileOffset, downloadBytes);
			InputStream in = storageSocket.getInputStream();
			header = ProtoCommon.recvHeader(in, ProtoCommon.STORAGE_PROTO_CMD_RESP, -1);
			this.errno = header.errno;
			if (header.errno != 0) {
				return header.errno;
			}

			byte[] buff = new byte[256 * 1024];
			long remainBytes = header.body_len;
			int bytes;

			while (remainBytes > 0) {
				if ((bytes = in.read(buff, 0, remainBytes > buff.length ? buff.length : (int) remainBytes)) < 0) {
					throw new IOException(
							"recv package size " + (header.body_len - remainBytes) + " != " + header.body_len);
				}

				os.write(buff, 0, bytes);
				remainBytes -= bytes;
			}
			return 0;
		} catch (IOException ex) {
			if (this.errno == 0) {
				this.errno = ProtoCommon.ERR_NO_EIO;
			}
			if (!bNewConnection) {
				try {
					this.storageServer.close();
				} catch (IOException ex1) {
					LOG.error("关闭server异常", ex1);
				} finally {
					this.storageServer = null;
				}
			}
			throw ex;
		} finally {
			if (bNewConnection) {
				try {
					this.storageServer.close();
				} catch (IOException e) {
					LOG.error("关闭server异常", e);
				} finally {
					this.storageServer = null;
				}
			}
		}
	}
	
	public int downloadFile2(String fileId, OutputStream out) throws IOException, MyException {
		final long fileOffset = 0;
		final long downloadBytes = 0;
		return this.downloadFile2(fileId, fileOffset, downloadBytes, out);
	}
	
	public int downloadFile2(String fileId, long fileOffset, long downloadBytes, OutputStream out) throws IOException, MyException {
		String[] parts = new String[2];
		this.errno = split_file_id(fileId, parts);
		if (this.errno != 0) {
			return this.errno;
		}
		return this.downloadFile(parts[0], parts[1], fileOffset, downloadBytes, out);
	}
	
}
